package gtug.duesseldorf.client;

import gtug.duesseldorf.client.model.ClientCommand;
import gtug.duesseldorf.client.model.ClientLoginData;
import gtug.duesseldorf.client.model.ClientParticipant;
import gtug.duesseldorf.client.model.ClientWaveId;
import gtug.duesseldorf.client.model.ClientWaveList;
import gtug.duesseldorf.client.ui.InputBox;
import gtug.duesseldorf.client.ui.LoginPanel;
import gtug.duesseldorf.client.ui.WaveContent;
import gtug.duesseldorf.client.ui.WaveList;

import java.util.List;

import net.zschech.gwt.comet.client.CometClient;
import net.zschech.gwt.comet.client.CometSerializer;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Wave_webclient implements EntryPoint {

	// TODO: move to config file
	// private static String default_username = "bar@mydomain.local";

	/**
	 * Create a remote service proxy to talk to the server-side Greeting service.
	 */
	private final GreetingServiceAsync m_service = GWT.create(GreetingService.class);

	private String m_clientId;

	private ClientWaveId m_openWaveId;

	private WaveContent m_tWavecontent;

	private WaveList m_lWaves;

	private InputBox m_inputBox;

	private LoginPanel m_loginPanel;

	private WaveServiceListener m_waveListener;

	public GreetingServiceAsync getService() {
		return m_service;
	}

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		Log.info("onModuleLoad");

		// define GUI

		FlexTable flexTable = new FlexTable();
		flexTable.setBorderWidth(1);
		flexTable.setWidth("100%");
		FlexCellFormatter tableFormatter = flexTable.getFlexCellFormatter();

		RootPanel.get().add(flexTable);

		// left side

		VerticalPanel leftSide = new VerticalPanel();
		flexTable.setWidget(0, 0, leftSide);
		tableFormatter.setWidth(0, 0, "25%");
		tableFormatter.setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP);

		HorizontalPanel buttonBarTop = new HorizontalPanel();
		leftSide.add(buttonBarTop);

		m_loginPanel = new LoginPanel();
		leftSide.add(m_loginPanel);

		final Button bConnect = new Button("connect");
		buttonBarTop.add(bConnect);

		Button bOpen = new Button("open");
		buttonBarTop.add(bOpen);

		Button bNew = new Button("new");
		buttonBarTop.add(bNew);

		m_lWaves = new WaveList(this);
		m_lWaves.setWidth("100%");
		leftSide.add(m_lWaves);

		// right side

		FlexTable rightSide = new FlexTable();
		rightSide.setBorderWidth(1);
		rightSide.setSize("100%", "100%");
		flexTable.setWidget(0, 1, rightSide);
		tableFormatter.setWidth(0, 1, "75%");
		tableFormatter.setHeight(0, 1, "100%");

		m_tWavecontent = new WaveContent(this);
		rightSide.setWidget(0, 0, m_tWavecontent);
		rightSide.getFlexCellFormatter().setHeight(0, 0, "99%");
		rightSide.getFlexCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP);

		m_inputBox = new InputBox(this);
		rightSide.setWidget(1, 0, m_inputBox);

		// LogData
		final Label lLoginStatus = new Label("Login Status: offline");
		flexTable.setWidget(1, 0, lLoginStatus);
		tableFormatter.setColSpan(1, 0, 2);

		// define actionListener

		ClickHandler chOpen = new ClickHandler() {
			public void onClick(ClickEvent event) {
				openWave(m_lWaves.getWaveId());
			}
		};
		bOpen.addClickHandler(chOpen);

		ClickHandler chConnect = new ClickHandler() {
			private CometClient m_cometClient;

			public void onClick(ClickEvent event) {
				if (m_clientId == null) {

					final ClientLoginData clientLoginData = m_loginPanel.getLoginData();
					if (clientLoginData == null) {
						Window.alert("Please enter the loginData");
						m_loginPanel.setFocus();
						return;
					}

					m_service.serverConnect(clientLoginData, new GenericAsyncCallback<String>() {

						@Override
						public void onSuccess(String clientId) {
							m_clientId = clientId;

							bConnect.setText("disconnect");
							lLoginStatus.setText("Login Status: online (" + clientLoginData + ") ClientId=" + m_clientId);

							CometSerializer serializer = GWT.create(WaveServiceSerializer.class);
							m_cometClient = new CometClient(GWT.getModuleBaseURL() + "comet?clientId=" + m_clientId, serializer, m_waveListener);
							m_cometClient.start();
						}
					});

				} else {
					m_service.serverDisconnect(m_clientId, new GenericAsyncCallback<Boolean>() {
						@Override
						public void onSuccess(Boolean result) {
							if (Boolean.TRUE.equals(result)) {
								bConnect.setText("connect");
								lLoginStatus.setText("Login Status: offline");

								// clear wave listing
								m_lWaves.clear();

								// clear wave view
								m_tWavecontent.clear();

								m_clientId = null;

								m_cometClient.stop();
							} else {
								// nothing happens on errors
							}
						}
					});
				}
			}
		};
		bConnect.addClickHandler(chConnect);

		ClickHandler chNew = new ClickHandler() {
			public void onClick(ClickEvent event) {
				if (notConnected()) {
					return;
				}

				Log.info("starting new wave");

				m_service.waveNew(m_clientId, new GenericAsyncCallback<Boolean>());
			}
		};
		bNew.addClickHandler(chNew);

		// init the listener of WaveChanges
		m_waveListener = new WaveServiceListener() {

			@Override
			protected void process(ClientCommand clientCommand) {
				ClientWaveList clientWaveList = clientCommand.getWaveList();

				if (clientWaveList != null) {
					Log.info("setWaveList: " + clientWaveList.toString());

					m_lWaves.setWaveList(clientWaveList);
				}

				if (clientCommand.isDoOpenWave()) {
					Log.info("setOpenWave: " + clientCommand.getWaveId());

					ClientWaveId clientWaveId = clientCommand.getWaveId();
					m_lWaves.setOpenWave(clientWaveId);
					m_tWavecontent.setOpenWave(clientWaveId);

					m_openWaveId = clientWaveId;
				}

				List<ClientParticipant> participantList = clientCommand.getParticipantList();
				if (participantList != null) {
					Log.info("applyParticipants: " + participantList.toString());

					m_tWavecontent.applyParticipants(participantList);
				}

				if (clientCommand.getMessageData() != null) {
					Log.info("applyData: " + clientCommand.getMessageData());
					m_tWavecontent.applyData(clientCommand);
				}

				m_inputBox.setFocus();
			}
		};

	}

	public void openWave(final String waveId) {
		if (notConnected()) {
			return;
		}

		if (waveId == null) {
			Window.alert("Please select a Wave to open");
			return;
		}

		Log.info("open wave id=" + waveId);

		m_service.waveOpen(m_clientId, waveId, new GenericAsyncCallback<Boolean>() {
			@Override
			public void onSuccess(Boolean result) {
				Log.info("success open wave id=" + waveId);
			}
		});
	}

	private boolean notConnected() {
		if (m_clientId == null) {
			Window.alert("Not Connected");
			return true;
		} else {
			return false;
		}
	}

	public void sendText(String txt) {
		if (alertNoOpenWave()) {
			return;
		}

		m_service.sendAppendMutation(m_clientId, m_openWaveId.getId(), txt, new GenericAsyncCallback<Void>());
	}

	public void addParticipant(String participantName) {
		if (alertNoOpenWave()) {
			return;
		}

		m_service.waveAddParticipant(m_clientId, m_openWaveId.getId(), participantName, new GenericAsyncCallback<Boolean>());
	}

	private boolean alertNoOpenWave() {
		if (m_openWaveId == null) {
			Window.alert("No Wave open. Please open a wave");
			return true;
		} else {
			return false;
		}
	}

	/**
	 * get the domain from {@link ClientLoginData}
	 * 
	 * @return
	 */
	public String getDomain() {
		ClientLoginData ld = m_loginPanel.getLoginData();
		if (ld == null) {
			return null;
		}

		return ld.getDomain();
	}

}
